// threadtest.cc 
//	Simple test case for the threads assignment.
//
//	Create two threads, and have them context switch
//	back and forth between themselves by calling Thread::Yield, 
//	to illustratethe inner workings of the thread system.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"
#ifdef CHANGED
#include "synch.h"
#include <math.h>
#include <vector.h>
#endif // CHANGED

#ifdef CHANGED
// constant for tweaking the algorithms
const int MAIN_IDLE_TICK = 10;			// number of ticks the main thread will yield for everything
const int MANAGER_IDLE_TICK = 10;		// number of ticks the manager thread will yield and check for customers
const int STOCKBOY_AISLE_TICK = 10;		// number of ticks the stock boy will stay in each aisle
const int STOCKBOY_STOCKROOM_TICK = 1;	// number of ticks the stock boy will take in the stock room to re-stock items.
const int MONEY_CHECK_TICK = 40;		// number of ticks between the time the manager will check the cash registers
const int ANNOUNCEMENT_TICK = 20;

int NUM_MANAGERS = 1;					// number of managers
int NUM_CLERKS = 3;						// number of clerks
int NUM_CUSTOMERS = 20;					// number of customers
int NUM_REGISTERS = 3;					// number of cash registers
int NUM_STOCKBOYS = 3;					// number of stock boys

int NUM_AISLES = 10;					// number of aisles/shelves
int INIT_ITEMS = 3;						// default number of items per aisles/shelves
int MIN_ITEMS = 3;						// default minimum of items before the stock boys are called to restock

int TEST_CASE = -1;						// switch for test case set ups

const int MAX_CLERKLINELENGTH = 4;		// max number of customers waiting in any line before more clerks are brought in

const int CLERK_IDLE_TICK = 20;
const int STOCKBOY_IDLE_TICK = 0;

const int WAIT_TO_HELP = 1;
const int NOT_WAIT_TO_HELP = -1;

enum WorkStatus { AT_WORK, ON_BREAK };
#endif // CHANGED

//----------------------------------------------------------------------
// SimpleThread
// 	Loop 5 times, yielding the CPU to another ready thread 
//	each iteration.
//
//	"which" is simply a number identifying the thread, for debugging
//	purposes.
//----------------------------------------------------------------------

void
SimpleThread(int which)
{
    int num;
    
    for (num = 0; num < 5; num++) {
	printf("*** thread %d looped %d times\n", which, num);
        currentThread->Yield();
    }
}

//----------------------------------------------------------------------
// ThreadTest
// 	Set up a ping-pong between two threads, by forking a thread 
//	to call SimpleThread, and then calling SimpleThread ourselves.
//----------------------------------------------------------------------

void
ThreadTest()
{
    DEBUG('t', "Entering SimpleTest");

    Thread *t = new Thread("forked thread");

    t->Fork(SimpleThread, 1);
    SimpleThread(0);
}

#ifdef CHANGED
// --------------------------------------------------
// Problem2()
//     This is the main thread of the part II of project 1.
// --------------------------------------------------
// monitor for cleaning up threads
Lock *cleanupLock;						// the lock for cleaning up the threads
Condition *cleanupCV;					// CV for clean
bool cleanupFlag = FALSE;
int cleanupThreads = 0;

// monitor for stopping simulation
Lock *customerLock;						// the lock for the number of customers
int customerThreads = 0;				// the number of customers coming in

// monitor for clerk wait line monitor variables
Lock *clerkLineLock;					// main lock for looking at clerk lines
vector<Condition *> clerkLineCVs;		// specific clerk line wait CV
vector<WorkStatus> clerkStates;			// clerk status, either at work or on break
vector<int> clerkLineLengths;			// the number of customers waiting in each line
bool clerkWakeupFlag = FALSE;			// the flag to wake up more clerks
Condition *clerkWakeupCV;				// the CV for waiting for clerks to come back to work
int clerkWakeupID = -1;
List *clerkWakeupIDs;
int clerkWakeupLine = 0;
Condition *clerkOnbreakCV;				// the CV for clerks on break
int clerkOnbreakLine = 0;

//monitor for clerk specific wait line monitor variables
vector<Lock *> clerkLineLocks;			// locks to each individual clerk line
vector<Condition *> clerkLineTransCVs;	// clerk-customer interaction CV
vector<int> clerkLineCustomerIDs;		// indicating the customer that is in transaction with clerk
vector<int> clerkLineItems;				// item handling between customer and clerk
vector<int> clerkLinePayments;			// indicating the money transfer from customer to clerk
vector<int> clerkLineTotals;			// indicating the total amount the items cost
vector<int> clerkLineCashes;			// indicating the money transfer from clerk to customer
vector<bool> clerkLineReturns;			// flag to tell if the transaction is return or purchase

// monitors for registers
vector<Lock *> registerLocks;
vector<Condition *> registerCVs;		// register CV between clerk and register
vector<Condition *> registerEmptyCVs;	// register CV between manager and register, for emptying the register
vector<Condition *> registerMoneyCVs;	// register CV between manager and register, for asking for money
vector<bool> registerReturns;			// flag to tell if the transaction is return or purchase
vector<int> registerCustomerIDs;		// mv telling the register the customer ID from clerk
vector<int> registerItems;				// item handling between clerk to register
vector<int> registerTotals;				// mv between clerk and register, the total cost of items
vector<int> registerPayments;			// mv between clerk and register, the money paid by customers
vector<int> registerCashes;				// mv between clerk and register, the money paid to customers
vector<int> registerLines;				// indicating if a clerk is waiting for a register
vector<int> registerEmptyLines;			// indicating if a manager is waiting to emptying register
vector<int> registerMoneyLines;			// indicating if a register is waiting to get money from manager
vector<int> registerMoneys;				// mv between manager and register, how much money register has right now
vector<int> registerManagerMoneys;		// mv between manager and register, the money the manager is giving to register

// monitors for stock boys and restocking
Lock *stockBoyStatusLock;
Condition *stockBoyOnbreakCV;			// the CV for stock boys on break
int stockBoyOnbreakLine = 0;
vector<WorkStatus> stockBoyStates;
vector<int> stockBoyAisles;
vector<int> stockBoyAisleHelps;
vector<Lock *> stockBoyLocks;
vector<Condition *> stockBoyCVs;
vector<int> stockBoyItems;
vector<int> stockBoyCustomerIDs;

// monitors for stock boys and customers in channel helping, the extra locks/conditions/lines are for the customers that cannot find available stock boys in all the aisles, thus in the common waiting area
vector<Lock *> aisleHelpLocks;
vector<Condition *> aisleHelpCVs;
vector<int> aisleHelpLines;
bool aisleHelpFlag = FALSE;

// monitor for aisles and aisle items
Lock *aisleLock;						// the lock for the aisles
vector <Lock *> aisleLocks;				// the lock for each aisle shelf
vector<Condition *> aisleItemCVs;		// the CV for waiting for the aisle shelf to be restocked
vector<int> aisleItems;					// the number of items per aisle shelf
vector<int> aisleRestockItems;			// the number of items to be restocked per aisle shelf

// monitor for stock room
Lock *stockRoomLock;
Lock *stockRoomWaitLock;
Condition *stockRoomWaitCV;
int stockRoomStockBoyID;
int stockRoomWaitLine = 0;

// aisle/shelf item prices
vector<int> itemPrices;

// test cases switches

void Initializes(int manager, int clerks, int customers, int registers, int stockboys, int aisles, int initial_items, int minimum_items) {
	char *name;

	NUM_MANAGERS = manager;
	NUM_CLERKS = clerks;
	NUM_CUSTOMERS = customers;
	NUM_REGISTERS = registers;
	NUM_STOCKBOYS = stockboys;
	NUM_AISLES = aisles;
	INIT_ITEMS = initial_items;
	MIN_ITEMS = minimum_items;

	//thread shutdown related monitor objects
	cleanupLock = new Lock("Cleanup Lock");
	cleanupCV = new Condition("Cleanup CV");
	cleanupFlag = FALSE;
	cleanupThreads = 0;

	//customer related monitor objects
	customerLock = new Lock("Customers Lock");
	customerThreads = 0;

	//clerk line related monitor objects
	clerkLineLock = new Lock("Clerk Line Lock");
	clerkStates.assign(NUM_CLERKS, ON_BREAK);
	clerkLineLengths.assign(NUM_CLERKS, 0);
	for (int i = 0; i < NUM_CLERKS; i++) {
		name = new char[30];
		sprintf(name, "Clerk Line %d CV", i);
		clerkLineCVs.push_back(new Condition(name));
		clerkStates[i] = ON_BREAK;
		clerkLineLengths[i] = 0;
	}
	clerkWakeupFlag = FALSE;
	name = new char[30];
	sprintf(name, "Clerk Wakeup CV");
	clerkWakeupCV = new Condition(name);
	clerkWakeupID = -1;
	clerkWakeupIDs = new List;
	clerkWakeupLine = 0;
	name = new char[30];
	sprintf(name, "Clerk Onbreak CV");
	clerkOnbreakCV = new Condition(name);
	clerkOnbreakLine = 0;

	//individual clerk line related monitor objects
	clerkLineCustomerIDs.assign(NUM_CLERKS, -1);
	clerkLineItems.assign(NUM_CLERKS, -1);
	clerkLinePayments.assign(NUM_CLERKS, 0);
	clerkLineTotals.assign(NUM_CLERKS, 0);
	clerkLineCashes.assign(NUM_CLERKS, 0);
	clerkLineReturns.assign(NUM_CLERKS, FALSE);
	for (int i = 0; i < NUM_CLERKS; i++) {
		name = new char[30];
		sprintf(name, "Clerk Line %d Lock", i);
		clerkLineLocks.push_back(new Lock(name));
		name = new char[50];
		sprintf(name, "Clerk Line %d Transaction CV", i);
		clerkLineTransCVs.push_back(new Condition(name));
		clerkLineCustomerIDs[i] = -1;
		clerkLineItems[i] = -1;
		clerkLinePayments[i] = 0;
		clerkLineTotals[i] = 0;
		clerkLineCashes[i] = 0;
		clerkLineReturns[i] = FALSE;
	}

	//individual cash register related monitor objects
	registerReturns.assign(NUM_REGISTERS, FALSE);
	registerCustomerIDs.assign(NUM_REGISTERS, -1);
	registerItems.assign(NUM_REGISTERS, -1);
	registerPayments.assign(NUM_REGISTERS, 0);
	registerCashes.assign(NUM_REGISTERS, 0);
	registerLines.assign(NUM_REGISTERS, 0);
	registerEmptyLines.assign(NUM_REGISTERS, 0);
	registerMoneyLines.assign(NUM_REGISTERS, 0);
	registerTotals.assign(NUM_REGISTERS, 0);
	registerMoneys.assign(NUM_REGISTERS, 0);
	registerManagerMoneys.assign(NUM_REGISTERS, 0);
	for (int i = 0; i < NUM_REGISTERS; i++) {
		name = new char[30];
		sprintf(name, "Cash Register %d", i);
		registerLocks.push_back(new Lock(name));
		name = new char[30];
		sprintf(name, "Cash Register %d CV", i);
		registerCVs.push_back(new Condition(name));
		name = new char[30];
		sprintf(name, "Cash Register %d Empty CV", i);
		registerEmptyCVs.push_back(new Condition(name));
		name = new char[30];
		sprintf(name, "Cash Register %d Money CV", i);
		registerMoneyCVs.push_back(new Condition(name));
		registerReturns[i] = FALSE;
		registerCustomerIDs[i] = -1;
		registerItems[i] = -1;
		registerPayments[i] = 0;
		registerCashes[i] = 0;
		registerLines[i] = 0;
		registerEmptyLines[i] = 0;
		registerMoneyLines[i] = 0;
		registerTotals[i] = 0;
		registerMoneys[i] = 0;
		registerManagerMoneys[i] = 0;
	}

	//individual aisle/shelf related monitor objects
	name = new char[30];
	sprintf(name, "Aisle Lock");
	aisleLock = new Lock(name);
	aisleItems.assign(NUM_AISLES, INIT_ITEMS);
	aisleRestockItems.assign(NUM_AISLES, 0);
	for (int i = 0; i < NUM_AISLES; i++) {
		name = new char[30];
		sprintf(name, "Aisle %d Lock", i);
		aisleLocks.push_back(new Lock(name));
		//aisleLocks[i] = new Lock(name);
		name = new char[30];
		sprintf(name, "aisle %d CV", i);
		aisleItemCVs.push_back(new Condition(name));
		//aisleItemCVs[i] = new Condition(name);
		aisleItems[i] = INIT_ITEMS;
		aisleRestockItems[i] = 0;
	}

	//stock room related monitor objects
	name = new char[30];
	sprintf(name, "Stockroom Lock");
	stockRoomLock = new Lock(name);
	name = new char[30];
	sprintf(name, "Stockroom Wait Lock");
	stockRoomWaitLock = new Lock(name);
	name = new char[30];
	sprintf(name, "Stockroom Wait CV");
	stockRoomWaitCV = new Condition(name);
	stockRoomStockBoyID = -1;
	stockRoomWaitLine = 0;

	//aisle line helps and stuff
	aisleHelpLines.assign((NUM_AISLES+1), 0);
	for (int i = 0; i < (NUM_AISLES+1); i++) {
		name = new char[30];
		sprintf("name", "Aisle %d Help Lock", i);
		aisleHelpLocks.push_back(new Lock(name));
		//aisleHelpLocks[i] = new Lock(name);
		name = new char[30];
		sprintf(name, "Aisle %d Help CV", i);
		aisleHelpCVs.push_back(new Condition(name));
		//aisleHelpCVs[i] = new Condition(name);
		aisleHelpLines[i] = 0;
	}
	aisleHelpFlag = FALSE;

	//stock boy related monitor objects
	name = new char[30];
	sprintf(name, "StockBoy Status Lock");
	stockBoyStatusLock = new Lock(name);
	name = new char[30];
	sprintf(name, "StockBoy Onbreak CV");
	stockBoyOnbreakCV = new Condition(name);
	stockBoyOnbreakLine = 0;
	stockBoyStates.assign(NUM_STOCKBOYS, ON_BREAK);
	stockBoyAisles.assign(NUM_STOCKBOYS, -1);
	stockBoyAisleHelps.assign(NUM_STOCKBOYS, -1);
	stockBoyItems.assign(NUM_STOCKBOYS, -1);
	stockBoyCustomerIDs.assign(NUM_STOCKBOYS, -1);
	for (int i = 0; i < NUM_STOCKBOYS; i++) {
		name = new char[30];
		sprintf(name, "StockBoy %d Lock", i);
		stockBoyLocks.push_back(new Lock(name));
		//stockBoyLocks[i] = new Lock(name);
		name = new char[30];
		sprintf(name, "StockBoy %d CV", i);
		stockBoyCVs.push_back(new Condition(name));
		//stockBoyCVs[i] = new Condition(name);
		stockBoyStates[i] = ON_BREAK;
		stockBoyAisles[i] = -1;
		stockBoyAisleHelps[i] = -1;
		stockBoyItems[i] = -1;
		stockBoyCustomerIDs[i] = -1;
	}

	itemPrices.assign(NUM_AISLES, -1);
	for (int i = 0; i < NUM_AISLES; i++) {
		itemPrices[i] = rand()%10 + 1;
	}

if (TEST_CASE == 1 || TEST_CASE == 2 || TEST_CASE == 3 || TEST_CASE == 4 || TEST_CASE == 5) {
	for (int i = 0; i < NUM_AISLES; i++) {
		itemPrices[i] = (i+1);
	}
}

	printf("Number of Managers = [%d]\n", NUM_MANAGERS);
	printf("Number of Clerks = [%d]\n", NUM_CLERKS);
	printf("Number of Customers = [%d]\n", NUM_CUSTOMERS);
	printf("Number of CashRegisters = [%d]\n", NUM_REGISTERS);
	printf("Number of StockBoys = [%d]\n", NUM_STOCKBOYS);
	printf("Number of Aisle = [%d]\n", NUM_AISLES);
	printf("Items:\n");
	for (int i = 0; i < NUM_AISLES; i++) {
		printf("[%d] - $[%d]\n", i, itemPrices[i]);
	}
}

void Cleanups() {
	//thread shutdown related monitor objects
	delete cleanupLock;
	delete cleanupCV;
	//printf("HERE1\n");

	//customer related monitor objects
	delete customerLock;

	//clerk line related monitor objects
	delete clerkLineLock;
	for (int i = 0; i < NUM_CLERKS; i++) {
		delete clerkLineCVs.back();
		clerkLineCVs.pop_back();
		//delete clerkLineCVs[i];
	}
	delete clerkWakeupCV;
	delete clerkWakeupIDs;
	delete clerkOnbreakCV;
	//printf("HERE2\n");

	//individual clerk line related monitor objects
	for (int i = 0; i < NUM_CLERKS; i++) {
		delete clerkLineLocks.back();
		clerkLineLocks.pop_back();
		//delete clerkLineLocks[i];
		delete clerkLineTransCVs.back();
		clerkLineTransCVs.pop_back();
		//delete clerkLineTransCVs[i];
	}
	//printf("HERE3\n");

	//individual cash register related monitor objects
	for (int i = 0; i < NUM_REGISTERS; i++) {
		delete registerLocks.back();
		registerLocks.pop_back();
		//delete registerLocks[i];
		delete registerCVs.back();
		registerCVs.pop_back();
		//delete registerCVs[i];
		delete registerEmptyCVs.back();
		registerEmptyCVs.pop_back();
		//delete registerEmptyCVs[i];
		delete registerMoneyCVs.back();
		registerMoneyCVs.pop_back();
		//delete registerMoneyCVs[i];
	}
	//printf("HERE4\n");

	//individual aisle/shelf related monitor objects
	delete aisleLock;
	for (int i = 0; i < NUM_AISLES; i++) {
		delete aisleLocks.back();
		aisleLocks.pop_back();
		//delete aisleLocks[i];
		delete aisleItemCVs.back();
		aisleItemCVs.pop_back();
		//delete aisleItemCVs[i];
	}
	//printf("HERE5\n");

	for (int i = 0; i < NUM_AISLES+1; i++) {
		delete aisleHelpLocks.back();
		aisleHelpLocks.pop_back();
		//delete aisleHelpLocks[i];
		delete aisleHelpCVs.back();
		aisleHelpCVs.pop_back();
		//delete aisleHelpCVs[i];
	}
	//printf("HERE6\n");

	//stock room related monitor objects	
	delete stockRoomLock;
	delete stockRoomWaitLock;
	delete stockRoomWaitCV;

	//stock boy related monitor objects
	delete stockBoyStatusLock;
	delete stockBoyOnbreakCV;
	for (int i = 0; i < NUM_STOCKBOYS; i++) {
		delete stockBoyLocks.back();
		stockBoyLocks.pop_back();
		//delete stockBoyLocks[i];
		delete stockBoyCVs.back();
		stockBoyCVs.pop_back();
		//delete stockBoyCVs[i];
	}
	//printf("HERE8\n");
}

void Clerk(int clerk_id) {
	int breakCounter = 0;

	//printf("Clerk %d is coming to work.\n", clerk_id);
	cleanupLock->Acquire();
	cleanupThreads++;
	cleanupLock->Release();

	clerkLineLock->Acquire();
	clerkStates[clerk_id] = AT_WORK;
	clerkLineLock->Release();

	while (TRUE) {
if (TEST_CASE != 1) {
		//check to see if more than 4 customers waiting in line
		clerkLineLock->Acquire();
		if (clerkLineLengths[clerk_id] >= MAX_CLERKLINELENGTH) {
			printf("Clerk [%d] sees more than %d people in line and calls Manager.\n", clerk_id, MAX_CLERKLINELENGTH);
			clerkWakeupFlag = TRUE;
			clerkWakeupIDs->Append((void *)&clerk_id);
			//clerkWakeupID = clerk_id;
			//clerkWakeupLine++;
			//clerkWakeupCV->Wait(clerkLineLock);
			//clerkWakeupLine--;
		}

		//check to see if customers are waiting fo this particular line
		if (clerkLineLengths[clerk_id] > 0) {
			breakCounter = 0;
			//signal first customer waiting in this line
			//printf("Clerk [%d] is signaling the customer waiting in line\n", clerk_id);
			clerkLineCVs[clerk_id]->Signal(clerkLineLock);

			//grab own lock to start customer-clerk interaction
			clerkLineLocks[clerk_id]->Acquire();
			clerkLinePayments[clerk_id] = 0;
			clerkLineTotals[clerk_id] = 0;
			clerkLineCashes[clerk_id] = 0;
			clerkLineLock->Release();

			//grab the register first
			registerLocks[clerk_id]->Acquire();
			registerLines[clerk_id]++;
			registerCVs[clerk_id]->Wait(registerLocks[clerk_id]);
			registerLines[clerk_id]--;

			//wait for the customer to tell if it's a purchase or return
			clerkLineItems[clerk_id] = 999;
			clerkLineTransCVs[clerk_id]->Wait(clerkLineLocks[clerk_id]);

			//tell the register the type of transaction purchase or return
			registerReturns[clerk_id] = clerkLineReturns[clerk_id];
			registerCustomerIDs[clerk_id] = clerkLineCustomerIDs[clerk_id];
			registerCVs[clerk_id]->Signal(registerLocks[clerk_id]);
			registerCVs[clerk_id]->Wait(registerLocks[clerk_id]);

			while (clerkLineItems[clerk_id] != -1) {
				//wait for customer to hand over item
				//clerkLineTransCVs[clerk_id]->Wait(clerkLineLocks[clerk_id]);
				//signal customer to hand over another item
				printf("Clerk [%d] got item [%d] from Customer [%d].\n", clerk_id, clerkLineItems[clerk_id], clerkLineCustomerIDs[clerk_id]);
				clerkLineTransCVs[clerk_id]->Signal(clerkLineLocks[clerk_id]);
				//clerkLineTransCVs[clerk_id]->Wait(clerkLineLocks[clerk_id]);

				//signal register to scan items
				registerItems[clerk_id] = clerkLineItems[clerk_id];
				printf("Clerk [%d] gave item [%d] to Cash Register [%d] for scanning.\n", clerk_id, clerkLineItems[clerk_id], clerk_id);
				//printf("Clerk [%d] is signaling Register to scan item [%d]\n", clerk_id, clerkLineItems[clerk_id]);
				registerCVs[clerk_id]->Signal(registerLocks[clerk_id]);
				//printf("Clerk [%d] is waiting for Register to scan item [%d]\n", clerk_id, clerkLineItems[clerk_id]);
				registerCVs[clerk_id]->Wait(registerLocks[clerk_id]);
				
				clerkLineTransCVs[clerk_id]->Wait(clerkLineLocks[clerk_id]);
			}

			// tell the register all items are done, wait for the total $
			registerItems[clerk_id] = clerkLineItems[clerk_id];
			registerCVs[clerk_id]->Signal(registerLocks[clerk_id]);
			registerCVs[clerk_id]->Wait(registerLocks[clerk_id]);
			clerkLineTotals[clerk_id] = registerTotals[clerk_id];
			printf("Clerk [%d] got total cost $[%d] from Cash Register [%d].\n", clerk_id, registerTotals[clerk_id], clerk_id);

			//signal for the customer to hand over items
			//clerkLineTransCVs[clerk_id]->Signal(clerkLineLocks[clerk_id]);
			//printf("Clerk [%d] is waiting for the customer to hand over items\n", clerk_id);
			//clerkLineTransCVs[clerk_id]->Wait(clerkLineLocks[clerk_id]);
			
			// transaction is return, signal the register it knows, and wait for the money from clerk
			if (clerkLineReturns[clerk_id]) {
				printf("Clerk [%d] tells Customer [%d] total cost is $[%d].\n", clerk_id, clerkLineCustomerIDs[clerk_id], registerTotals[clerk_id]);
				registerCVs[clerk_id]->Signal(registerLocks[clerk_id]);
				registerCVs[clerk_id]->Wait(registerLocks[clerk_id]);
				printf("Clerk [%d] got money $[%d] from Cash Register [%d].\n", clerk_id, registerCashes[clerk_id], clerk_id);

				clerkLineCashes[clerk_id] = registerCashes[clerk_id];
				printf("Clerk [%d] gave the money $[%d] to Customer [%d].\n", clerk_id, clerkLineCashes[clerk_id], clerkLineCustomerIDs[clerk_id]);
				// wait for customer to acknowledge the money received
				clerkLineTransCVs[clerk_id]->Signal(clerkLineLocks[clerk_id]);
				clerkLineTransCVs[clerk_id]->Wait(clerkLineLocks[clerk_id]);

				// signal the cash register for the receipt
				registerCVs[clerk_id]->Signal(registerLocks[clerk_id]);
				registerCVs[clerk_id]->Wait(registerLocks[clerk_id]);
			}
			// transaction is purchase, wait for payment
			else {
				clerkLineTotals[clerk_id] = registerTotals[clerk_id];
				printf("Clerk [%d] tells Customer [%d] total cost is $[%d].\n", clerk_id, clerkLineCustomerIDs[clerk_id], clerkLineTotals[clerk_id]);
				clerkLineTransCVs[clerk_id]->Signal(clerkLineLocks[clerk_id]);
				clerkLineTransCVs[clerk_id]->Wait(clerkLineLocks[clerk_id]);

				printf("Clerk [%d] got money $[%d] from Customer [%d].\n", clerk_id, clerkLinePayments[clerk_id], clerkLineCustomerIDs[clerk_id]);
				registerPayments[clerk_id] = clerkLinePayments[clerk_id];
				printf("Clerk [%d] gave the money $[%d] to Cash Register [%d].\n", clerk_id, registerPayments[clerk_id], clerk_id);
				registerCVs[clerk_id]->Signal(registerLocks[clerk_id]);
				registerCVs[clerk_id]->Wait(registerLocks[clerk_id]);
			}

			//give customer the recipt
			printf("Clerk [%d] gave the receipt to Customer [%d] and tells him to leave.\n", clerk_id, clerkLineCustomerIDs[clerk_id]);
			clerkLineTransCVs[clerk_id]->Signal(clerkLineLocks[clerk_id]);
			clerkLineTransCVs[clerk_id]->Wait(clerkLineLocks[clerk_id]);

			// release the register
			registerLocks[clerk_id]->Release();

			// release the lock, we are done with this customer
			clerkLineLocks[clerk_id]->Release();
		}
		else {
			breakCounter++;

			if (breakCounter >= CLERK_IDLE_TICK) {
				breakCounter = 0;
				//leave the register and go on break
				printf("Clerk [%d] is leaving Cash Register [%d] and going on break.\n", clerk_id, clerk_id);
				clerkStates[clerk_id] = ON_BREAK;
				clerkOnbreakLine++;
				clerkOnbreakCV->Wait(clerkLineLock);
				cleanupLock->Acquire();
				if (!cleanupFlag) {
					printf("Clerk [%d] was called by Manager to work.\n", clerk_id);
				}
				cleanupLock->Release();
				clerkOnbreakLine--;
				clerkStates[clerk_id] = AT_WORK;
				clerkLineLock->Release();
			}
			else {
				clerkLineLock->Release();
			}
		}
}

		//if no more customers, stop the while loop, otherwise, continue
		cleanupLock->Acquire();
		if (cleanupFlag) {
			cleanupLock->Release();
			break;
		}
		cleanupLock->Release();

		currentThread->Yield();
	}

	cleanupLock->Acquire();
	cleanupThreads--;
	cleanupCV->Signal(cleanupLock);
	cleanupLock->Release();
	//printf("Clerk %d leaves.\n", clerk_id);
}

void Customer(int customer_id) {
	int shortestLineIndex = -1;
	int shortestLineLength = 999;

	vector<int> items;
	int totalItems = 0;
	int stockBoyAisle = -1;
	bool returnFlag = FALSE;
	bool stockBoyFoundFlag = FALSE;
	int totalItemCount = 0;
	int aisleHelpStockBoyID = -1;

	//printf("Customer [%d] is coming to shop.\n", customer_id);
	cleanupLock->Acquire();
	cleanupThreads++;
	cleanupLock->Release();
	
	//increment the customer counter every time a customer enters
	customerLock->Acquire();
	customerThreads++;
	customerLock->Release();

	//set all items to -1 as no item
	/*
	items.assign(NUM_AISLES, -1);
	for (int i = 0; i < NUM_AISLES; i++) {
		items[i] = -1;
	}
	*/

	totalItems = rand()%10 + 1;
	//totalItems = 1;

	//randomize 1 to 10 items to buy
	items.assign(totalItems, -1);
	for (int i = 0; i < totalItems; i++) {
		items[i] = rand()%NUM_AISLES;
		//items[i] = 0;
	}

	//determine the 20% of the time the customer return items
	if (rand()%5 == 0) {
		returnFlag = TRUE;
	}

	//randomize 25% of the time the customer will need to find items from stock boys
	//plus NUM_AISLES to indicate the fact that the item aisle will be asked
	if (!returnFlag) {
		for (int i = 0; i < totalItems; i++) {
			if (rand()%4 == 0) {
				items[i] += NUM_AISLES;
			}
		}
	}
	/*
	printf("Item:");
	for (int i = 0; i < totalItems; i++) {
		if (items[i] != -1) {
			printf(" [%d]", items[i]);
		}
	}
	printf("\n");
	*/

if (TEST_CASE == 1 || TEST_CASE == 4 || TEST_CASE == 5) {
	totalItems = 1;
	items[0] = 0;
	if (TEST_CASE != 5) {
		returnFlag = TRUE;
	}
	else {
		items[0] = NUM_AISLES;
	}
}
else if (TEST_CASE == 3) {
	totalItems = 3;
	items[0] = 1;
	items[1] = 2;
	items[2] = 3;
	returnFlag = TRUE;
}

	if (returnFlag) {
		printf("Customer [%d] needs to return [%d] no.of items.\n", customer_id, totalItems);
	}
	else {
		printf("Customer [%d] wants to buy [%d] no.of items.\n", customer_id, totalItems);
	}

	//find the items in aisles
	if (!returnFlag) {
		for (int i = 0; i < totalItems; i++) {
			// if the items aisle/shelf is negative, find stock boys to help
			if (items[i] != -1) {
				stockBoyAisle = -1;
	
				//the item aisle number >= NUM_AISLES; ie, dunno where to find the item, try to find a stock boy
				if (items[i] >= NUM_AISLES) {
					printf("Customer [%d] is not able to find item [%d] and is searching for StockBoy.\n", customer_id, items[i]-NUM_AISLES);
					for (int j = 0; j < NUM_AISLES; j++) {
						aisleHelpLocks[j]->Acquire();

						//now we are at the aisle, look for stock boys
						stockBoyStatusLock->Acquire();
						for (int k = 0; k < NUM_STOCKBOYS; k++) {
							if (stockBoyStates[k] == AT_WORK && stockBoyAisles[k] == j) {
								stockBoyAisle = j;
								break;
							}
						}
						stockBoyStatusLock->Release();

						if (stockBoyAisle != -1) {
							//printf("Customer [%d] is waiting to get help at aisle [%d]\n", customer_id, stockBoyAisle);
							aisleHelpLines[stockBoyAisle]++;
							aisleHelpCVs[stockBoyAisle]->Wait(aisleHelpLocks[stockBoyAisle]);

							//find a stock boy that signaled to help
							stockBoyStatusLock->Acquire();
							for (int k = 0; k < NUM_STOCKBOYS; k++) {
								if (stockBoyStates[k] == AT_WORK && stockBoyAisleHelps[k] == WAIT_TO_HELP && stockBoyAisles[k] == stockBoyAisle) {
									aisleHelpStockBoyID = k;
									stockBoyAisleHelps[k] = NOT_WAIT_TO_HELP;
									break;
								}
							}
							stockBoyStatusLock->Release();

							stockBoyLocks[aisleHelpStockBoyID]->Acquire();
							aisleHelpLocks[stockBoyAisle]->Release();

							//if (aisleHelpStockBoyID == -1) {
							//	printf("*********************************SOMETHING IS TOTALLY WRONG================\n");
							//	aisleHelpStockBoyID = 0;
							//}

							//printf("Lane %d StockBoy [%d] stockBoyAisleHelps [%d] stockBoyAisles [%d]\n", stockBoyAisle, aisleHelpStockBoyID, stockBoyAisleHelps[aisleHelpStockBoyID], stockBoyAisles[aisleHelpStockBoyID]);
							printf("Customer [%d] is asking for assistance from StockBoy [%d].\n", customer_id, aisleHelpStockBoyID);
							stockBoyCustomerIDs[aisleHelpStockBoyID] = customer_id;
							stockBoyCVs[aisleHelpStockBoyID]->Signal(stockBoyLocks[aisleHelpStockBoyID]);
							
							stockBoyCVs[aisleHelpStockBoyID]->Wait(stockBoyLocks[aisleHelpStockBoyID]);

							//give the stock boy the item and ask for the aisle
							stockBoyItems[aisleHelpStockBoyID] = items[i];
							stockBoyCVs[aisleHelpStockBoyID]->Signal(stockBoyLocks[aisleHelpStockBoyID]);

							//wait for the stockboy to give the proper aisle for the item
							stockBoyCVs[aisleHelpStockBoyID]->Wait(stockBoyLocks[aisleHelpStockBoyID]);
							items[i] = stockBoyItems[aisleHelpStockBoyID];

							printf("Customer [%d] got aisle [%d] from StockBoy [%d].\n", customer_id, items[i], aisleHelpStockBoyID);
							
							//items[i] -= NUM_AISLES;
							stockBoyLocks[aisleHelpStockBoyID]->Release();
							break;
						}
						else {
							aisleHelpLocks[j]->Release();
						}
					}

					//cannot find an available stock boy in aisles, call manager
					if (stockBoyAisle == -1) {
						printf("Customer [%d] looked in all aisle for StockBoy. He is now requesting help by pressing service button.\n", customer_id);
						aisleHelpLocks[NUM_AISLES]->Acquire();
						aisleHelpFlag = TRUE;
						aisleHelpLines[NUM_AISLES]++;
						aisleHelpCVs[NUM_AISLES]->Wait(aisleHelpLocks[NUM_AISLES]);

						//printf("HERE [%d] should be one per ....\n", customer_id);
						//find a stock boy that signaled to help
						stockBoyStatusLock->Acquire();
						for (int j = 0; j < NUM_STOCKBOYS; j++) {
							if (stockBoyStates[j] == AT_WORK && stockBoyAisleHelps[j] == WAIT_TO_HELP && stockBoyAisles[j] == NUM_AISLES) {
								aisleHelpStockBoyID = j;
								stockBoyAisleHelps[j] = NOT_WAIT_TO_HELP;
								break;
							}
						}
						stockBoyStatusLock->Release();

						stockBoyLocks[aisleHelpStockBoyID]->Acquire();
						aisleHelpLocks[NUM_AISLES]->Release();

						//if (aisleHelpStockBoyID == -1) {
						//	printf("*********************************SOMETHING IS TOTALLY WRONG================\n");
						//	aisleHelpStockBoyID = 0;
						//}

						//printf("Lane EXTRA StockBoy [%d] stockBoyAisleHelps [%d] stockBoyAisles [%d]\n", aisleHelpStockBoyID, stockBoyAisleHelps[aisleHelpStockBoyID], stockBoyAisles[aisleHelpStockBoyID]);
						printf("Customer [%d] is asking for assistance from StockBoy [%d].\n", customer_id, aisleHelpStockBoyID);
						stockBoyCustomerIDs[aisleHelpStockBoyID] = customer_id;
						stockBoyCVs[aisleHelpStockBoyID]->Signal(stockBoyLocks[aisleHelpStockBoyID]);
						
						stockBoyCVs[aisleHelpStockBoyID]->Wait(stockBoyLocks[aisleHelpStockBoyID]);

						//give the stock boy the item and ask for the aisle
						stockBoyItems[aisleHelpStockBoyID] = items[i];
						stockBoyCVs[aisleHelpStockBoyID]->Signal(stockBoyLocks[aisleHelpStockBoyID]);

						//wait for the stockboy to give the proper aisle for the item
						stockBoyCVs[aisleHelpStockBoyID]->Wait(stockBoyLocks[aisleHelpStockBoyID]);
						items[i] = stockBoyItems[aisleHelpStockBoyID];

						printf("Customer [%d] got aisle [%d] from StockBoy [%d].\n", customer_id, items[i], aisleHelpStockBoyID);
						
						//items[i] -= NUM_AISLES;
						stockBoyLocks[aisleHelpStockBoyID]->Release();
					}
					
					if (items[i] >= NUM_AISLES) {
						//printf("*************Customer [%d] got aisle for item [%d]....from stockboy wrong\n", customer_id, items[i]);
						items[i] -= NUM_AISLES;
					}
				}

				// get the lock for the aisle/shelf before taking the items
				aisleLocks[items[i]]->Acquire();
				while (aisleItems[items[i]] == 0) {
					//printf("Customer [%d] is waiting for item [%d] to be restocked.\n", customer_id, items[i]);
					aisleItemCVs[items[i]]->Wait(aisleLocks[items[i]]);
					//printf("Customer [%d] is trying to grab item [%d].\n", customer_id, items[i]);
				}
				//printf("Customer [%d] grabs the item [%d].\n", customer_id, items[i]);
				aisleItems[items[i]]--;
				aisleLocks[items[i]]->Release();
			}
		}
	}

	//find the clerk with shortest line
if (TEST_CASE != 5) {
	clerkLineLock->Acquire();
	while (TRUE) {
		shortestLineIndex = -1;
		shortestLineLength = 999;

		//loop through all the clerk wait lines
		for (int i = 0; i < NUM_CLERKS; i++) {
			// if the clerk is on break, find another one
			if (clerkStates[i] == ON_BREAK) {
				continue;
			}

			// check to see if the current wait line is shorter than the wait line we have in mind
			if (clerkLineLengths[i] < shortestLineLength) {
				shortestLineIndex = i;
				shortestLineLength = clerkLineLengths[i];
			}
		}

		// all clerks are on break, call manager
		if (shortestLineIndex == -1) {
			//printf("Customer %d cannot find an available clerk, calling Manager\n", customer_id);
			clerkWakeupFlag = TRUE;
			clerkWakeupLine++;
			//printf("Customer %d is waiting for an available clerk\n", customer_id);
			clerkWakeupCV->Wait(clerkLineLock);
			clerkWakeupLine--;
			//printf("Customer %d is finding a clerk again\n", customer_id);
		}
		else {
			break;
		}
	}
}
	//proceed to the clerk with shortest line
if (TEST_CASE != 5) {
	printf("Customer [%d] chose Clerk [%d] with line length of [%d].\n", customer_id, shortestLineIndex, shortestLineLength);
	clerkLineLengths[shortestLineIndex]++;
}
if (TEST_CASE != 1 && TEST_CASE != 5) {
	//printf("Customer %d is waiting in line to clerk %d\n", customer_id, shortestLineIndex);
	clerkLineCVs[shortestLineIndex]->Wait(clerkLineLock);
	clerkLineLengths[shortestLineIndex]--;

	//grab the individual clerk line and tell the clerk the purchase/return type, and signal the clerk
	clerkLineLocks[shortestLineIndex]->Acquire();
	clerkLineLock->Release();
	clerkLineCustomerIDs[shortestLineIndex] = customer_id;
	clerkLineReturns[shortestLineIndex] = returnFlag;


	//Hand over all the items one at a time
	for (int i = 0; i < totalItems; i++) {
		if (items[i] != -1) {
			clerkLineItems[shortestLineIndex] = items[i];
			printf("Customer [%d] gives item [%d] to Clerk [%d].\n", customer_id, items[i], shortestLineIndex);
			clerkLineTransCVs[shortestLineIndex]->Signal(clerkLineLocks[shortestLineIndex]);
			//printf("Customer [%d] is signaling to hand over item no. [%d] to clerk [%d]\n", customer_id, items[i], shortestLineIndex);
			clerkLineTransCVs[shortestLineIndex]->Wait(clerkLineLocks[shortestLineIndex]);
		}
	}

	//printf("Customer %d is signaling to hand over items to clerk %d\n", customer_id, shortestLineIndex);
	clerkLineItems[shortestLineIndex] = -1;
	clerkLineTransCVs[shortestLineIndex]->Signal(clerkLineLocks[shortestLineIndex]);
	printf("Customer [%d] tells Clerk [%d], I have given you all my [%d] items.\n", customer_id, shortestLineIndex, totalItems);
	clerkLineTransCVs[shortestLineIndex]->Wait(clerkLineLocks[shortestLineIndex]);

	//pay for the itmes or receive money from item
	// if customer is returning items, no need to pay, one less wait/signal
	if (returnFlag) {
		printf("Customer [%d] receives $[%d] from Clerk [%d] and is now waiting for receipt.\n", customer_id, clerkLineCashes[shortestLineIndex], shortestLineIndex);
	}
	//wait for the clerk to give the total cost and pay
	else {
		clerkLinePayments[shortestLineIndex] = clerkLineTotals[shortestLineIndex];
		printf("Customer [%d] pays $[%d] to Clerk [%d] and is now waiting for receipt.\n", customer_id, clerkLineTotals[shortestLineIndex], shortestLineIndex);
	}
	clerkLineTransCVs[shortestLineIndex]->Signal(clerkLineLocks[shortestLineIndex]);

	//wait for the recipt
	clerkLineTransCVs[shortestLineIndex]->Wait(clerkLineLocks[shortestLineIndex]);
	printf("Customer [%d] got receipt from Clerk [%d] and is now leaving.\n", customer_id, shortestLineIndex);
	clerkLineTransCVs[shortestLineIndex]->Signal(clerkLineLocks[shortestLineIndex]);

	clerkLineLocks[shortestLineIndex]->Release();
}
else if (TEST_CASE != 5) {
	clerkLineLock->Release();
}	

	//decrement the customer counter every time a customer leaves
	customerLock->Acquire();
	customerThreads--;
	customerLock->Release();

	//thread shutdown, 
	cleanupLock->Acquire();
	cleanupThreads--;
	cleanupCV->Signal(cleanupLock);
	cleanupLock->Release();
	//printf("Customer %d leaves.\n", customer_id);
}

void StockBoy(int stockboy_id) {
	int restockAisleIndex = -1;
	bool restockFlag = FALSE;
	bool helpFlag = FALSE;

	//printf("StockBoy [%d] is coming to work.\n", stockboy_id);
	cleanupLock->Acquire();
	cleanupThreads++;
	cleanupLock->Release();

	stockBoyStatusLock->Acquire();
	stockBoyStates[stockboy_id] = AT_WORK;
	stockBoyStatusLock->Release();

	while (TRUE) {
		restockAisleIndex = -1;
		restockFlag = FALSE;
		helpFlag = FALSE;

		//if people are waiting to be helped, help them first.
		aisleHelpLocks[NUM_AISLES]->Acquire();
		while (aisleHelpLines[NUM_AISLES] > 0) {
			helpFlag = TRUE;
			// if there are customers waiting in the aisle to be helped, signal one of them
			//printf("SB [%d] found someone to help, put myself on the list, let them find me,\n", stockboy_id);
			stockBoyStatusLock->Acquire();
			stockBoyAisles[stockboy_id] = NUM_AISLES;
			stockBoyAisleHelps[stockboy_id] = WAIT_TO_HELP;
			stockBoyStatusLock->Release();

			//signal anyone waiting for help, one of them
			aisleHelpCVs[NUM_AISLES]->Signal(aisleHelpLocks[NUM_AISLES]);
			aisleHelpLines[NUM_AISLES]--;
				
			//grab my own lock, wait for them to signal me
			stockBoyLocks[stockboy_id]->Acquire();
			aisleHelpLocks[NUM_AISLES]->Release();
			stockBoyCVs[stockboy_id]->Wait(stockBoyLocks[stockboy_id]);
			
			printf("StockBoy [%d] is approached by Customer [%d] for assistance.\n", stockboy_id, stockBoyCustomerIDs[stockboy_id]);
			stockBoyCVs[stockboy_id]->Signal(stockBoyLocks[stockboy_id]);

			// wait for the customer to give the item that needs to be found.
			stockBoyCVs[stockboy_id]->Wait(stockBoyLocks[stockboy_id]);
			stockBoyItems[stockboy_id] -= NUM_AISLES;
			printf("StockBoy [%d] gave aisle [%d] to Customer [%d] for item [%d].\n", stockboy_id, stockBoyItems[stockboy_id], stockBoyCustomerIDs[stockboy_id], stockBoyItems[stockboy_id]);
			stockBoyCVs[stockboy_id]->Signal(stockBoyLocks[stockboy_id]);
			
			stockBoyLocks[stockboy_id]->Release();

			//re-acquire the help line lock
			aisleHelpLocks[NUM_AISLES]->Acquire();
		}
		stockBoyStatusLock->Acquire();
		stockBoyAisles[stockboy_id] = -1;
		stockBoyAisleHelps[stockboy_id] = NOT_WAIT_TO_HELP;
		stockBoyStatusLock->Release();
		aisleHelpLocks[NUM_AISLES]->Release();

		//find items to restock, first aisle that requires restocking will do
		printf("StockBoy [%d] is checking if more items are to be re-stocked.\n", stockboy_id);
		for (int i = 0; i < NUM_AISLES; i++) {
			aisleLocks[(i+stockboy_id) % NUM_AISLES]->Acquire();
			//if the items on an aisle/shelf are lower than the threshold (including ones that are going to be restocked by other stockboys.
			if ((aisleItems[(i+stockboy_id) % NUM_AISLES] + aisleRestockItems[(i+stockboy_id) % NUM_AISLES]) < MIN_ITEMS) {
				printf("StockBoy [%d] goes back to stock room for item [%d].\n", stockboy_id, (i+stockboy_id) % NUM_AISLES);
				restockFlag = TRUE;
				restockAisleIndex = (i+stockboy_id) % NUM_AISLES;
				aisleRestockItems[(i+stockboy_id) % NUM_AISLES]++;
				aisleLocks[(i+stockboy_id) % NUM_AISLES]->Release();
				break;
			}
			aisleLocks[(i+stockboy_id) % NUM_AISLES]->Release();
		}

		//restock items one at a time from aisle for stock room to aisle
		if (restockAisleIndex != -1) {
			stockRoomWaitLock->Acquire();
			while (stockRoomStockBoyID != -1) {
				// if the stockRoomStockBoyID isn't -1, then that means someone is already in the stock room, wait
				//if (stockRoomStockBoyID != -1) {
					printf("StockBoy [%d] is waiting for StockBoy [%d] to leave the stock room.\n", stockboy_id, stockRoomStockBoyID);
					//stockRoomWaitLine++;
					stockRoomWaitCV->Wait(stockRoomWaitLock);
					//stockRoomWaitLine--;
				//}
				//else {
				//	break;
				//}
			}

			// acquire the stock room lock and update stock boy ID
			stockRoomLock->Acquire();
			printf("StockBoy [%d] is in stock room.\n", stockboy_id);
			stockRoomStockBoyID = stockboy_id;
			stockRoomWaitLock->Release();

			printf("StockBoy [%d] retrieve one new item [%d] from the stock room.\n", stockboy_id, restockAisleIndex);
			for (int i = 0; i < STOCKBOY_STOCKROOM_TICK; i++) {
				currentThread->Yield();
			}

			//wake up the stock boys waiting to get into stock room
			stockRoomWaitLock->Acquire();
			stockRoomLock->Release();
			printf("StockBoy [%d] leaves stock room.\n", stockboy_id);
			stockRoomStockBoyID = -1;
			stockRoomWaitCV->Broadcast(stockRoomWaitLock);
			stockRoomWaitLock->Release();

			//leave the stock room to go to aisle
			stockBoyStatusLock->Acquire();
			//printf("StockBoy [%d] is at aisle [%d] to restock.\n", stockboy_id, restockAisleIndex);
			stockBoyAisles[stockboy_id] = restockAisleIndex;
			stockBoyStatusLock->Release();
			
			//simulate the time the stock boy takes in an aisle before restocking an item
			for (int i = 0; i < STOCKBOY_AISLE_TICK; i++) {
				//printf("StockBoy [%d] tick [%d]\n", stockboy_id, i);
				currentThread->Yield();
			}

			//check to see if customers need help in the aisle I am restocking the item
			aisleHelpLocks[restockAisleIndex]->Acquire();
			while (aisleHelpLines[restockAisleIndex] > 0) {
				helpFlag = TRUE;
				stockBoyStatusLock->Acquire();
				stockBoyAisleHelps[stockboy_id] = WAIT_TO_HELP;
				stockBoyStatusLock->Release();

				//signal anyone waiting for help, one of them
				aisleHelpCVs[restockAisleIndex]->Signal(aisleHelpLocks[restockAisleIndex]);
				aisleHelpLines[restockAisleIndex]--;
				
				//grab my own lock, wait for them to signal me
				stockBoyLocks[stockboy_id]->Acquire();
				aisleHelpLocks[restockAisleIndex]->Release();
				stockBoyCVs[stockboy_id]->Wait(stockBoyLocks[stockboy_id]);
				
				printf("StockBoy [%d] is approached by Customer [%d] for assistance.\n", stockboy_id, stockBoyCustomerIDs[stockboy_id]);
				stockBoyCVs[stockboy_id]->Signal(stockBoyLocks[stockboy_id]);

				// wait for the customer to give the item that needs to be found.
				stockBoyCVs[stockboy_id]->Wait(stockBoyLocks[stockboy_id]);
				stockBoyItems[stockboy_id] -= NUM_AISLES;
				printf("StockBoy [%d] gave aisle [%d] to Customer [%d] for item [%d].\n", stockboy_id, stockBoyItems[stockboy_id], stockBoyCustomerIDs[stockboy_id], stockBoyItems[stockboy_id]);
				stockBoyCVs[stockboy_id]->Signal(stockBoyLocks[stockboy_id]);
				
				stockBoyLocks[stockboy_id]->Release();

				//re-acquire the help line lock
				aisleHelpLocks[restockAisleIndex]->Acquire();
			}	
			stockBoyStatusLock->Acquire();
			stockBoyAisles[stockboy_id] = -1;
			stockBoyStatusLock->Release();
			aisleHelpLocks[restockAisleIndex]->Release();

			//restock item
			//printf("StockBoy [%d] is at aisle [%d] restocking item [%d].\n", stockboy_id, restockAisleIndex, restockAisleIndex);
			aisleLocks[restockAisleIndex]->Acquire();
			aisleItems[restockAisleIndex]++;
			aisleRestockItems[restockAisleIndex]--;
			aisleItemCVs[restockAisleIndex]->Broadcast(aisleLocks[restockAisleIndex]);
			aisleLocks[restockAisleIndex]->Release();
		}

		if (!restockFlag && !helpFlag) {
			stockBoyStatusLock->Acquire();
			printf("StockBoy [%d] is is going on break.\n", stockboy_id);
			stockBoyAisles[stockboy_id] = -1;
			stockBoyStates[stockboy_id] = ON_BREAK;
			stockBoyOnbreakLine++;
			stockBoyOnbreakCV->Wait(stockBoyStatusLock);
			stockBoyOnbreakLine--;
			cleanupLock->Acquire();
			if (!cleanupFlag) {
				printf("StockBoy [%d] got call from Manager.\n", stockboy_id);
			}
			cleanupLock->Release();
			stockBoyAisles[stockboy_id] = -1;
			stockBoyStates[stockboy_id] = AT_WORK;
			stockBoyStatusLock->Release();
		}

		//if no more customers, stop the while loop, otherwise, continue
		cleanupLock->Acquire();
		if (cleanupFlag) {
			cleanupLock->Release();
			break;
		}
		cleanupLock->Release();
		
		currentThread->Yield();
	}

	cleanupLock->Acquire();
	cleanupThreads--;
	cleanupCV->Signal(cleanupLock);
	cleanupLock->Release();
	//printf("Stock Boy %d leaves.\n", stockboy_id);
}

void Register(int register_id) {
	int items = 0;
	int itemsTotal = 0;
	int difference = 0;

	//printf("Cash Register %d is coming online.\n", id);
	cleanupLock->Acquire();
	cleanupThreads++;
	cleanupLock->Release();

if (TEST_CASE == 2) {
	registerLocks[register_id]->Acquire();
	registerMoneys[register_id] = (register_id+1) * 100;
	registerLocks[register_id]->Release();
}

	while (TRUE) {
		registerLocks[register_id]->Acquire();
		//wait for the clerk / manager to use the register
if (TEST_CASE != 2) {
		// if a clerk requests the register, respond
		if (registerLines[register_id] > 0) {
			items = 0;
			itemsTotal = 0;
			registerItems[register_id] = -1;
			registerPayments[register_id] = 0;
			registerTotals[register_id] = 0;
			registerCashes[register_id] = 0;
			registerManagerMoneys[register_id] = 0;
			registerCustomerIDs[register_id] = -1;
			registerReturns[register_id] = FALSE;

			// clerk is waiting response from register, signal
			registerCVs[register_id]->Signal(registerLocks[register_id]);
			registerCVs[register_id]->Wait(registerLocks[register_id]);

			// now wait for the clerk to provide purchase or return flag
			/*
			if (registerReturns[register_id]) {
				printf("Register [%d] got return type...\n", register_id);
			}
			else {
				printf("Register [%d] got purchase type...\n", register_id);
			}
			*/
			registerCVs[register_id]->Signal(registerLocks[register_id]);
			registerCVs[register_id]->Wait(registerLocks[register_id]);

			// wait for the clerk to hand over items and calculate the cost
			while (registerItems[register_id] != -1) {
				items++;
				//registerCVs[id]->Wait(registerLocks[id]);
				printf("Cash Register [%d] got item [%d] from Clerk [%d] to scan.\n", register_id, registerItems[register_id], register_id);
				itemsTotal += itemPrices[registerItems[register_id]];
				//printf("Cash Register [%d] : $[%d]\n", register_id, itemsTotal);
				registerCVs[register_id]->Signal(registerLocks[register_id]);
				//printf("Register [%d] is waiting for scanning next item\n", register_id);
				registerCVs[register_id]->Wait(registerLocks[register_id]);
			}
			printf("Cash Register [%d] has scanned all [%d] items for Customer [%d].\n", register_id, items, registerCustomerIDs[register_id]);

			// now put the calculated cost to the m.v., give to clerk
			registerTotals[register_id] = itemsTotal;
			printf("Cash Register [%d] gave total cost $[%d] for Customer [%d] to Clerk [%d] for [%d] items.\n", register_id, registerTotals[register_id], registerCustomerIDs[register_id], register_id, items);
			registerCVs[register_id]->Signal(registerLocks[register_id]);
			registerCVs[register_id]->Wait(registerLocks[register_id]);

			// if the transaction type is return, check to see if the register has enough money, or ask manager
			if (registerReturns[register_id]) {
				registerMoneys[register_id] -= registerTotals[register_id];
				registerCashes[register_id] = registerTotals[register_id];
				clerkLineCashes[register_id] = registerCashes[register_id];

				// if the cash register does not have enough money, ask manager
				if (registerMoneys[register_id] < 0) {
					printf("Cash Register [%d] informed the manager that he needs $[%d].\n", register_id, registerMoneys[register_id]*-1);
					registerMoneyLines[register_id]++;
					// if manager is waiting for me, wake up him and let him give me money first
					if (registerEmptyLines[register_id] > 0) {
						registerEmptyCVs[register_id]->Signal(registerLocks[register_id]);
					}
					registerMoneyCVs[register_id]->Wait(registerLocks[register_id]);
					printf("Cash Register [%d] received $[%d] from the manager.\n", register_id, registerManagerMoneys[register_id]);
					registerMoneys[register_id] += registerManagerMoneys[register_id];
					registerManagerMoneys[register_id] = 0;
				}

				printf("Cash Register [%d] gave the money to Clerk [%d].\n", register_id, register_id);
				registerCVs[register_id]->Signal(registerLocks[register_id]);
				registerCVs[register_id]->Wait(registerLocks[register_id]);
			}
			// otherwise, ask the clerk to tell customer to pay up
			else {
				//wait for the money
				if (registerPayments[register_id] == registerTotals[register_id]) {
					printf("Cash Register [%d] got the money from Clerk [%d].\n", register_id, register_id);
					registerMoneys[register_id] += registerPayments[register_id];
					registerPayments[register_id] = 0;
				}
				// this line shouldn't happen, but anyway
				else {
					// ERROR
					//ASSERT(FALSE);
					printf("Cash Register [%d] got the money from Clerk [%d].\n", register_id, register_id);
					registerMoneys[register_id] += registerPayments[register_id];
					registerPayments[register_id] = 0;
				}
			}

			// give the receipt to clerk, and we are done
			printf("Cash Register [%d] gave receipt to Clerk [%d] for Customer [%d].\n", register_id, register_id, registerCustomerIDs[register_id]);
			registerCVs[register_id]->Signal(registerLocks[register_id]);

			printf("Cash Register [%d] total sale is $[%d].\n", register_id, registerMoneys[register_id]);
		}
}

		// if manager is waiting to empty the cash register, response
		if (registerEmptyLines[register_id] > 0) {
			registerEmptyCVs[register_id]->Signal(registerLocks[register_id]);
			registerEmptyLines[register_id] = 0;
			registerEmptyCVs[register_id]->Wait(registerLocks[register_id]);
			
			if (registerMoneys[register_id] > 0) {
				printf("Cash Register [%d] drawer is emptied by Manager.\n", register_id);
				printf("Cash Register [%d] total sale until now is $[%d].\n", register_id, registerMoneys[register_id]);
				registerMoneys[register_id] = 0;
			}
		}

		registerLocks[register_id]->Release();

		//if no more customers, stop the while loop, otherwise, continue
		cleanupLock->Acquire();
		if (cleanupFlag) {
			cleanupLock->Release();
			break;
		}
		cleanupLock->Release();

		currentThread->Yield();
	}

	//decrement the running thread counter so the main thread knows when to clean up
	cleanupLock->Acquire();
	cleanupThreads--;
	cleanupCV->Signal(cleanupLock);
	cleanupLock->Release();
	//printf("Cash Register %d is offline.\n", register_id);
}

void Manager() {
	int periodCounter = 0;
	int shutdownCounter = 0;
	vector<int> sales;
	int salesTotal = 0;
	bool stockBoyWakeupFlag = FALSE;

	//printf("Manager is coming to work.\n");
	cleanupLock->Acquire();
	cleanupThreads++;
	cleanupLock->Release();

	sales.assign(NUM_REGISTERS, 0);
	for (int i = 0; i < NUM_REGISTERS; i++) {
		sales[i] = 0;
	}

	while (TRUE) {
		stockBoyWakeupFlag = FALSE;

		//random announcement of the sales, 5% of the time
		if (rand()%ANNOUNCEMENT_TICK == 0) {
			printf("-------Total Sale of the entire store until now is $[%d]--------\n", salesTotal);
		}

		//check registers for money
		periodCounter++;
		periodCounter %= MONEY_CHECK_TICK;

if (TEST_CASE == 2) {
	periodCounter = 0;
}

		for (int i = 0; i < NUM_REGISTERS; i++) {
			registerLocks[i]->Acquire();

			if (periodCounter == 0) {
				registerEmptyLines[i]++;
			}

			// if register needs money from manager
			if (registerMoneyLines[i] > 0) {
				sales[i] += registerMoneys[i];
				salesTotal += registerMoneys[i];
				printf("Manager gave $[%d] to Cash Register [%d].\n", registerMoneys[i]*-1, i);
				//printf("***Manager now has $[%d].\n", salesTotal);
				registerManagerMoneys[i] = registerMoneys[i]*-1;
				registerMoneyCVs[i]->Signal(registerLocks[i]);
				registerMoneyLines[i] = 0;
			}

			// empty the cash from the registers periodically
			if (periodCounter == 0) {
				registerEmptyCVs[i]->Wait(registerLocks[i]);

				// if the register is waiting money from manager, give money first.
				if (registerMoneyLines[i] > 0) {
					sales[i] += registerMoneys[i];
					salesTotal += registerMoneys[i];
					printf("Manager gave $[%d] to Cash Register [%d].\n", registerMoneys[i]*-1, i);
					registerManagerMoneys[i] = registerMoneys[i]*-1;
					registerMoneyCVs[i]->Signal(registerLocks[i]);
					registerMoneyLines[i] = 0;
					registerEmptyCVs[i]->Wait(registerLocks[i]);
				}

				if (registerMoneys[i] > 0) {
					sales[i] += registerMoneys[i];
					salesTotal += registerMoneys[i];
					//registerMoneys[i] = 0;
					printf("Manager emptied Cash Register [%d] drawer.\n", i);
					printf("Manager has total sale of $[%d].\n", salesTotal);
				}
				registerEmptyLines[i] = 0;
				registerEmptyCVs[i]->Signal(registerLocks[i]);
			}

			registerLocks[i]->Release();
		}

		//if customers are waiting for clerks to be available, wake up a clerk
		//printf("Manager is checking to see if need to wake up more clerks.\n");
		clerkLineLock->Acquire();
		if (clerkWakeupFlag) {
			while (!clerkWakeupIDs->IsEmpty()) {
				printf("Manager got a call from Clerk [%d].\n", *(int *)clerkWakeupIDs->Remove());
			}
			clerkWakeupID = -1;
			if (clerkOnbreakLine > 0) {
				printf("Manager wakes up Clerk.\n");
				clerkOnbreakCV->Broadcast(clerkLineLock);
			}
			if (clerkWakeupLine > 0) {
				clerkWakeupCV->Broadcast(clerkLineLock);
			}
			clerkWakeupFlag = FALSE;
		}
		clerkLineLock->Release();

		//check the stock level and set the internal flag
		for (int i = 0; i < NUM_AISLES; i++) {
			aisleLocks[i]->Acquire();
			if ((aisleItems[i] + aisleRestockItems[i]) < MIN_ITEMS) {
				stockBoyWakeupFlag = TRUE;
				aisleLocks[i]->Release();
				break;
			}
			aisleLocks[i]->Release();
		}

		//check help service button and set the internal flag
		aisleHelpLocks[NUM_AISLES]->Acquire();
		if (aisleHelpFlag) {
			stockBoyWakeupFlag = TRUE;
			aisleHelpFlag = FALSE;
		}
		aisleHelpLocks[NUM_AISLES]->Release();

		//wake up stock boys if internal flag is set
		stockBoyStatusLock->Acquire();
		if (stockBoyWakeupFlag) {
			if (stockBoyOnbreakLine > 0) {
				printf("Manager wakes up StockBoy.\n");
				stockBoyOnbreakCV->Broadcast(stockBoyStatusLock);
			}
			stockBoyWakeupFlag = FALSE;
		}
		stockBoyStatusLock->Release();

		//check to see if there is no more customer before shutting down
		customerLock->Acquire();
		if (customerThreads == 0) {
			shutdownCounter++;
		}
		else {
			shutdownCounter = 0;
		}
		customerLock->Release();

		//if no more customers during set ticks, set the monitor variable so other threads knows to stop looping
		if (shutdownCounter > MANAGER_IDLE_TICK) {
			//turn the shutdown flag to true
			cleanupLock->Acquire();
			cleanupFlag = TRUE;
			cleanupLock->Release();

			//check to see if any clerks are sleeping
			clerkLineLock->Acquire();
			if (clerkOnbreakLine > 0) {
				clerkOnbreakCV->Broadcast(clerkLineLock);
			}
			clerkLineLock->Release();

			//check to see if any stockboys are sleeping
			stockBoyStatusLock->Acquire();
			if (stockBoyOnbreakLine > 0) {
				stockBoyOnbreakCV->Broadcast(stockBoyStatusLock);
			}
			stockBoyStatusLock->Release();
		}

		cleanupLock->Acquire();
		if (cleanupFlag) {
			cleanupLock->Release();
			break;
		}
		cleanupLock->Release();

		currentThread->Yield();
	}
	
	//print out the total sales of the store
	for (int i = 0; i < NUM_REGISTERS; i++) {
		printf("Total Sale from Cash Register [%d] is $[%d].\n", i, sales[i]);
	}
	printf("Total Sale of the entire store is $[%d].\n", salesTotal);

	//decrement the running thread counter so the main thread knows when to clean up
	cleanupLock->Acquire();
	cleanupThreads--;
	cleanupCV->Signal(cleanupLock);
	cleanupLock->Release();
	//printf("Manager leaves.\n");
}

void TestCase1() {
	char *name;
    Thread *thread;
	int idleCounter = 1;
	int managers = 1;
	int clerks = 3;
	int customers = 15;
	int registers = 3;
	int stockboys = 3;
	int aisles = 10;
	int initial_items = 3;
	int minimum_items = 3;
	
	TEST_CASE = 1;
	customers = 4;
	clerks = 2;
	registers = 0;
	stockboys = 0;

	printf("====Home Depot Simulation Test Case 1====\n");
	Initializes(managers, clerks, customers, registers, stockboys, aisles, initial_items, minimum_items);

	for (int i = 0; i < managers; i++) {
		name = new char[30];
		sprintf(name, "Manager");
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)Manager, i);
	}

    for (int i = 0; i < clerks; i++) {
		name = new char[30];
		sprintf(name, "Clerk [%d]", i);
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)Clerk, i);
	}
	for (int i = 0; i < stockboys; i++) {
		name = new char[30];
		sprintf(name, "StockBoy [%d]", i);
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)StockBoy, i);
	}
	for (int i = 0; i < registers; i++) {
		name = new char[30];
		sprintf(name, "Cash Register [%d]", i);
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)Register, i);
	}
	for (int i = 0; i < customers; i++) {
		name = new char[30];
		sprintf(name, "Customer [%d]", i);
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)Customer, i);
	}

	// idling until all threads are proper forked and up-running
	while (idleCounter < MAIN_IDLE_TICK) {
		currentThread->Yield();
		idleCounter++;
	}

	// check to see if all threads have completed
	cleanupLock->Acquire();
	while (cleanupThreads > 0) {
		cleanupCV->Wait(cleanupLock);
	}
	cleanupLock->Release();

	Cleanups();
	printf("====Home Depot Simulation Test Case 1 Ends====\n");
}

void TestCase2() {
	char *name;
    Thread *thread;
	int idleCounter = 1;
	int managers = 1;
	int clerks = 3;
	int customers = 15;
	int registers = 3;
	int stockboys = 3;
	int aisles = 10;
	int initial_items = 3;
	int minimum_items = 3;
	
	TEST_CASE = 2;
	customers = 0;
	clerks = 0;
	registers = 3;
	stockboys = 0;

	printf("====Home Depot Simulation Test Case 2====\n");
	Initializes(managers, clerks, customers, registers, stockboys, aisles, initial_items, minimum_items);

	for (int i = 0; i < managers; i++) {
		name = new char[30];
		sprintf(name, "Manager");
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)Manager, i);
	}
    for (int i = 0; i < clerks; i++) {
		name = new char[30];
		sprintf(name, "Clerk [%d]", i);
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)Clerk, i);
	}
	for (int i = 0; i < stockboys; i++) {
		name = new char[30];
		sprintf(name, "StockBoy [%d]", i);
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)StockBoy, i);
	}
	for (int i = 0; i < registers; i++) {
		name = new char[30];
		sprintf(name, "Cash Register [%d]", i);
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)Register, i);
	}
	for (int i = 0; i < customers; i++) {
		name = new char[30];
		sprintf(name, "Customer [%d]", i);
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)Customer, i);
	}

	// idling until all threads are proper forked and up-running
	while (idleCounter < MAIN_IDLE_TICK) {
		currentThread->Yield();
		idleCounter++;
	}

	// check to see if all threads have completed
	cleanupLock->Acquire();
	while (cleanupThreads > 0) {
		cleanupCV->Wait(cleanupLock);
	}
	cleanupLock->Release();

	Cleanups();
	printf("====Home Depot Simulation Test Case 2 Ends====\n");
}

void TestCase3() {
	char *name;
    Thread *thread;
	int idleCounter = 1;
	int managers = 1;
	int clerks = 3;
	int customers = 15;
	int registers = 3;
	int stockboys = 3;
	int aisles = 10;
	int initial_items = 3;
	int minimum_items = 3;
	
	TEST_CASE = 3;
	customers = 2;
	clerks = 1;
	registers = 1;
	stockboys = 0;

	printf("====Home Depot Simulation Test Case 3====\n");
	Initializes(managers, clerks, customers, registers, stockboys, aisles, initial_items, minimum_items);

	for (int i = 0; i < managers; i++) {
		name = new char[30];
		sprintf(name, "Manager");
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)Manager, i);
	}
    for (int i = 0; i < clerks; i++) {
		name = new char[30];
		sprintf(name, "Clerk [%d]", i);
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)Clerk, i);
	}
	for (int i = 0; i < stockboys; i++) {
		name = new char[30];
		sprintf(name, "StockBoy [%d]", i);
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)StockBoy, i);
	}
	for (int i = 0; i < registers; i++) {
		name = new char[30];
		sprintf(name, "Cash Register [%d]", i);
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)Register, i);
	}
	for (int i = 0; i < customers; i++) {
		name = new char[30];
		sprintf(name, "Customer [%d]", i);
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)Customer, i);
	}

	// idling until all threads are proper forked and up-running
	while (idleCounter < MAIN_IDLE_TICK) {
		currentThread->Yield();
		idleCounter++;
	}

	// check to see if all threads have completed
	cleanupLock->Acquire();
	while (cleanupThreads > 0) {
		cleanupCV->Wait(cleanupLock);
	}
	cleanupLock->Release();

	Cleanups();
	printf("====Home Depot Simulation Test Case 3 Ends====\n");
}

void TestCase4() {
	char *name;
    Thread *thread;
	int idleCounter = 1;
	int managers = 1;
	int clerks = 3;
	int customers = 15;
	int registers = 3;
	int stockboys = 3;
	int aisles = 10;
	int initial_items = 3;
	int minimum_items = 3;
	
	TEST_CASE = 4;
	customers = 1;
	clerks = 2;
	registers = 2;
	stockboys = 0;

	printf("====Home Depot Simulation Test Case 4====\n");
	Initializes(managers, clerks, customers, registers, stockboys, aisles, initial_items, minimum_items);

	for (int i = 0; i < managers; i++) {
		name = new char[30];
		sprintf(name, "Manager");
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)Manager, i);
	}
    for (int i = 0; i < clerks; i++) {
		name = new char[30];
		sprintf(name, "Clerk [%d]", i);
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)Clerk, i);
	}
	for (int i = 0; i < stockboys; i++) {
		name = new char[30];
		sprintf(name, "StockBoy [%d]", i);
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)StockBoy, i);
	}
	for (int i = 0; i < registers; i++) {
		name = new char[30];
		sprintf(name, "Cash Register [%d]", i);
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)Register, i);
	}
	for (int i = 0; i < customers; i++) {
		name = new char[30];
		sprintf(name, "Customer [%d]", i);
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)Customer, i);
	}

	// idling until all threads are proper forked and up-running
	while (idleCounter < MAIN_IDLE_TICK) {
		currentThread->Yield();
		idleCounter++;
	}

	// idling a few more ticks then spawn customer threads
	idleCounter = 0;
	while (idleCounter < 20) {
		currentThread->Yield();
		idleCounter++;
	}

	for (int i = customers; i < customers * 10; i++) {
		name = new char[30];
		sprintf(name, "Customer [%d]", i);
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)Customer, i);
	}

	// idling a few more ticks then spawn customer threads
	idleCounter = 0;
	while (idleCounter < 10) {
		currentThread->Yield();
		idleCounter++;
	}

	for (int i = customers * 10; i < customers * 15; i++) {
		name = new char[30];
		sprintf(name, "Customer [%d]", i);
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)Customer, i);
	}

	// check to see if all threads have completed
	cleanupLock->Acquire();
	while (cleanupThreads > 0) {
		cleanupCV->Wait(cleanupLock);
	}
	cleanupLock->Release();

	Cleanups();
	printf("====Home Depot Simulation Test Case 4 Ends====\n");
}

void TestCase5() {
	char *name;
    Thread *thread;
	int idleCounter = 1;
	int managers = 1;
	int clerks = 3;
	int customers = 15;
	int registers = 3;
	int stockboys = 3;
	int aisles = 10;
	int initial_items = 3;
	int minimum_items = 3;
	
	TEST_CASE = 5;
	customers = 6;
	clerks = 0;
	registers = 0;
	stockboys = 2;
	aisles = 2;
	initial_items = 0;
	minimum_items = 1;

	printf("====Home Depot Simulation Test Case 5====\n");
	Initializes(managers, clerks, customers, registers, stockboys, aisles, initial_items, minimum_items);

	for (int i = 0; i < managers; i++) {
		name = new char[30];
		sprintf(name, "Manager");
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)Manager, i);
	}
    for (int i = 0; i < clerks; i++) {
		name = new char[30];
		sprintf(name, "Clerk [%d]", i);
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)Clerk, i);
	}
	for (int i = 0; i < stockboys; i++) {
		name = new char[30];
		sprintf(name, "StockBoy [%d]", i);
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)StockBoy, i);
	}
	for (int i = 0; i < registers; i++) {
		name = new char[30];
		sprintf(name, "Cash Register [%d]", i);
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)Register, i);
	}
	for (int i = 0; i < customers; i++) {
		name = new char[30];
		sprintf(name, "Customer [%d]", i);
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)Customer, i);
	}

	// idling until all threads are proper forked and up-running
	while (idleCounter < MAIN_IDLE_TICK) {
		currentThread->Yield();
		idleCounter++;
	}

	// check to see if all threads have completed
	cleanupLock->Acquire();
	while (cleanupThreads > 0) {
		cleanupCV->Wait(cleanupLock);
	}
	cleanupLock->Release();

	Cleanups();
	printf("====Home Depot Simulation Test Case 5 Ends====\n");
}

void TestCaseDefault() {
	char *name;
    Thread *thread;
	int idleCounter = 1;
	int managers = 1;
	int clerks = 3;
	int customers = 20;
	int registers = 3;
	int stockboys = 3;
	int aisles = 10;
	int initial_items = 3;
	int minimum_items = 3;
	
	TEST_CASE = 0;

	printf("====Home Depot Simulation Test Case DEFAULT====\n");
	Initializes(managers, clerks, customers, registers, stockboys, aisles, initial_items, minimum_items);
	for (int i = 0; i < NUM_AISLES; i++) {
		aisleItems[i] = 0;
	}

	for (int i = 0; i < managers; i++) {
		name = new char[30];
		sprintf(name, "Manager");
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)Manager, i);
	}
    for (int i = 0; i < clerks; i++) {
		name = new char[30];
		sprintf(name, "Clerk [%d]", i);
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)Clerk, i);
	}
	for (int i = 0; i < stockboys; i++) {
		name = new char[30];
		sprintf(name, "StockBoy [%d]", i);
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)StockBoy, i);
	}
	for (int i = 0; i < registers; i++) {
		name = new char[30];
		sprintf(name, "Cash Register [%d]", i);
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)Register, i);
	}
	for (int i = 0; i < customers; i++) {
		name = new char[30];
		sprintf(name, "Customer [%d]", i);
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)Customer, i);
	}

	// idling until all threads are proper forked and up-running
	while (idleCounter < MAIN_IDLE_TICK) {
		currentThread->Yield();
		idleCounter++;
	}

	// check to see if all threads have completed
	cleanupLock->Acquire();
	while (cleanupThreads > 0) {
		cleanupCV->Wait(cleanupLock);
	}
	cleanupLock->Release();

	Cleanups();
	printf("====Home Depot Simulation Test Case DEFAULT Ends====\n");
}

void Problem2(int test) {
	if (test == 0) {
		TestCaseDefault();
	}
	else if (test == 1) {
		TestCase1();
	}
	else if (test == 2) {
		TestCase2();
	}
	else if (test == 3) {
		TestCase3();
	}
	else if (test == 4) {
		TestCase4();
	}
	else if (test == 5) {
		TestCase5();
	}
	else {
		printf("Please provide a proper test case #.\n");
	}
}

void Problem2(int managers, int clerks, int customers, int registers, int stockboys) {
	char *name;
    Thread *thread;
	int idleCounter = 0;

	if (managers != 1) {
		printf("There must be one Manager in the store. Setting to 1.\n");
	}

	if (clerks < 0) {
		printf("There must be a at least 1 Clerk. Setting to 1.\n");
	}
	else if (clerks > 5) {
		printf("There must be at most 5 Clerks. Setting to 5.\n");
	}
		
	if (clerks != registers) {
		printf("There must be equal numbers of Clerks and Cash Registers. Setting to [%d].\n", clerks);
		registers = clerks;
	}

	if (stockboys < 3) {
		printf("There must be at least 3 Stock Boy in the store. Setting to 3.\n");
		stockboys = 3;
	}

	printf("====Home Depot Simulation Test Case CUSTOM====\n");
	Initializes(managers, clerks, customers, registers, stockboys, 10, 3, 3);

	for (int i = 0; i < managers; i++) {
		name = new char[30];
		sprintf(name, "Manager");
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)Manager, 0);
	}

    for (int i = 0; i < clerks; i++) {
		name = new char[30];
		sprintf(name, "Clerk [%d]", i);
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)Clerk, i);
	}

	for (int i = 0; i < stockboys; i++) {
		name = new char[30];
		sprintf(name, "StockBoy [%d]", i);
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)StockBoy, i);
	}

	for (int i = 0; i < registers; i++) {
		name = new char[30];
		sprintf(name, "Cash Register [%d]", i);
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)Register, i);
	}

	for (int i = 0; i < customers; i++) {
		name = new char[30];
		sprintf(name, "Customer [%d]", i);
		thread = new Thread(name);
		thread->Fork((VoidFunctionPtr)Customer, i);
	}

	// idling until all threads are proper forked and up-running
	while (idleCounter < MAIN_IDLE_TICK) {
		currentThread->Yield();
		idleCounter++;
	}

	// check to see if all threads have completed
	cleanupLock->Acquire();
	while (cleanupThreads > 0) {
		cleanupCV->Wait(cleanupLock);
	}
	cleanupLock->Release();

	Cleanups();
	printf("====Home Depot Simulation Test Case CUSTOM Ends====\n");
}
#endif // CHANGED
